/* 开始对 axios 进行二次封装
  目的: 可以继承 axios 并且扩展
  把 axios 的封装分成 两部分
    todo 1: 继承原先 axios 的能力, 并且加上可以扩展的能力
      => 作为 axios 我们自己的 类
      => 没有具体实现, 只是把能力扩展上
    todo 2: 根据我们自己的 类 去创建实例
      => 最终使用的事我们自己的 类 创建出来的实例
      => 只有在我们自己创建的 类 里面 才会他有具体实现
*/

// 对 axios 进行二次封装, 创建出我们自己的类
// 导入 axios 相关的内容
import axios, {
  AxiosRequestConfig,         // => axios 内置的请求相关配置
  AxiosInstance,              // => axios 的实例出来
  AxiosResponse,              // => axios 内置的响应类型
  InternalAxiosRequestConfig, // => axios 配置的请求相关参数
  AxiosRequestHeaders,        // => axios 的请求头
} from '@ohos/axios'

// 创建自己的 interface

// 1. interceptorHooks
interface InterceptorHooks {
  // 请求拦截器成功的 hook
  requestInterceptor?: (config: HttpRequestConfig) => Promise<HttpRequestConfig>
  // 请求拦截器失败的 hook
  requestInterceptorCatch?: (error: any) => any
  // 响应拦截器成功的 hook
  responseInterceptor?: (response: AxiosResponse) => Promise<AxiosResponse>
  // 响应拦截器失败的 hook
  responseInterceptorCatch?: (error: any) => any
}

// 2. 写一个请求配置项
// 说明: 是因为 axios 版本要求, 必须要设置请求头信息 header
//      但是我们请求的时候, 有时候不需要单独设置请求头, 就会没有 headers
//      所以, 我们忽略这个错误
// @ts-ignore
interface HttpRequestConfig extends InternalAxiosRequestConfig {
  // 今后你想在配置项内多加一个 needLoading
  needLoading?: boolean
  needRouter?: string
  interceptorHooks?: InterceptorHooks
  headers?: AxiosRequestHeaders | { token: string }
}


// 最终: 一个自己的类
export default class AxiosHttpRequest {
  // 必须要有配置项( 你原先的配置 )
  // config: axios 的 config 的类型
  config: HttpRequestConfig
  // 拦截器, 可有可无
  // 因为我们的类里面没有 拦截器功能, 所以我需要把它加上, 从 axios.create 里面把它拿出来放在 我的 class 里面, 就有这个功能了
  interceptorHooks?: InterceptorHooks
  // 本身实例是不可以添加 拦截器 的
  // 为了可以添加拦截器, 我们必须要有的事 axios 的实例
  instance: AxiosInstance

  // 我们在发送请求的时候, 想对 config 内扩展一些内容
  // 如果你写的类型限制是 InternalAxiosRequestConfig, 根本没法扩展
  // 我们需要自己写一个内容, 继承自 InternalAxiosRequestConfig, 才能扩展
  constructor (options: HttpRequestConfig) {
    this.config = options
    // 单独拿到所有的 interceptors, 因为他们需要单独给实例添加
    this.interceptorHooks = options.interceptorHooks
    // 需要一个axios 实例, 因为你想配置 baseURL 和一起其他的公共的内容, 那么需要有实力
    this.instance = axios.create(options)
    // 不管你有没有拦截器, 我应该在创建实例的时候, 去把拦截器加上(如果你有)
    this.setupInterceptors()
  }

  // todo : 将连接器设置在当前实例身上
  // 当你创建实例的时候, 如果有拦截器, 直接加上了, 如果没有拦截器, 就不用了
  setupInterceptors () {
    this.instance.interceptors.request.use(
      // axios 的版本要求, headers 表示必填选项
      // @ts-ignore
      this.interceptorHooks?.requestInterceptor,
      this.interceptorHooks?.requestInterceptorCatch
    )
    this.instance.interceptors.response.use(
      // 因为可能没有响应拦截器, 所以在这里使用一个 控制运算符
      this.interceptorHooks?.responseInterceptor,
      this.interceptorHooks?.responseInterceptorCatch
    )
  }

  // 准备一个 request 方法, 其实使用的就是 axios.request 这个方法
  // 作为一个基准方法
  request<T = any> (config: HttpRequestConfig): Promise<T> {
    return new Promise<T>((resolve, reject) => {
      this
        .instance
        .request<any, T>(config)
        .then(res => resolve(res))
        .catch(err => reject(err))
    })
  }

  // 就差 get / post / put / delete 这些方法了
  get<T = any> (config: HttpRequestConfig): Promise<T> {
    return this.request({ ...config, method: 'GET' })
  }

  post<T = any> (config: HttpRequestConfig): Promise<T> {
    return this.request({ ...config, method: 'POST' })
  }

  delete<T = any> (config: HttpRequestConfig): Promise<T> {
    return this.request({ ...config, method: 'DELETE' })
  }

  put<T = any> (config: HttpRequestConfig): Promise<T> {
    return this.request({ ...config, method: 'PUT' })
  }
}


